#include "NfcClient.h"

#include "CNDefMessageListener.h"
#include "NDefMessageHandle.h"
#include "XmlFactory.h"
#include "EnumImplementations.h"

#include "Log.h"

CNfcClient::CNfcClient()
    {
    // No implementation required
    }

CNfcClient::~CNfcClient()
    {
    CloseReceivedMessage();
    iMessageListener->CancelDiscovery();
    iSynchronousCallTimeout->Cancel();
    
    delete iMessageListener;
    delete iSynchronousCallTimeout;
    }

CNfcClient* CNfcClient::NewLC()
    {
    CNfcClient* self = new (ELeave) CNfcClient();
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
    }

CNfcClient* CNfcClient::NewL()
    {
    CNfcClient* self = CNfcClient::NewLC();
    CleanupStack::Pop(self);
    return self;
    }

void CNfcClient::ConstructL()
    {
    iMessageListener = CNdefMessageListener::NewL(*this);
    iSynchronousCallTimeout = CPeriodic::NewL(CActive::EPriorityStandard);
    }

void CNfcClient::NDefMessageReceivedL(RNdefMessageHandle & aMessage)
    {
    iReceivedMessage = &aMessage;
    CancelTagRead();
    }

void CNfcClient::NDefMessageReceptionFailedL()
    {
    CancelTagRead();
    }

void CNfcClient::ReadTagAsyncL(MFunctor<HBufC8 *> & aRet, TDesC8 const & aTnf, TDesC8 const & aType, TInt aTimeout)
    {
    LOG4(_L8("ReadTagAsyncL(%S, %S, %d)"), &aTnf, &aType, aTimeout);
    
    iMessageReturnFunctor = &aRet;
    // Start timeout
    TTimeIntervalMicroSeconds32 timeout(aTimeout * 1000 * 1000);
    iSynchronousCallTimeout->Start(timeout, timeout, TCallBack(&CNfcClient::CancelTagReadCallback, this));
    
    // Subscribe
    CNdefRecord::TNdefRecordTnf tnf = EnumConverter<CNdefRecord::TNdefRecordTnf>::Conv(aTnf);
    iMessageListener->StartNdefDiscoveryL(tnf, aType);
    }

void CNfcClient::SetForegroundL(TBool aOnForeground)
    {
    iMessageListener->SetForegroundL(aOnForeground);
    }

void CNfcClient::CancelTagRead()
    {
    iSynchronousCallTimeout->Cancel();
    iMessageListener->CancelDiscovery();
    (*iMessageReturnFunctor)(ReceivedMessageToBufL());
    iMessageReturnFunctor = NULL;
    }

void CNfcClient::CloseReceivedMessage()
    {
    if(iReceivedMessage) { iReceivedMessage->Close(); }
    iReceivedMessage = NULL;
    }

HBufC8 * CNfcClient::ReceivedMessageToBufL()
    {
    if(!iReceivedMessage) { return _L8("Timed out").AllocL(); } // TODO
        
    CleanupClosePushL(*iReceivedMessage);
    
    RXmlFactory factory;
    factory.OpenL();
    CleanupClosePushL(factory);
    factory.AddDataL(*iReceivedMessage);
    
    RBuf8 buf;
    buf.CleanupClosePushL();
    factory.ToBuffer(buf);
    // TODO better memory management?
    HBufC8 * ret = buf.AllocL(); 
    
    // Clean up
    CleanupStack::PopAndDestroy(3, iReceivedMessage);
    iReceivedMessage = NULL;
    
    LOG1(*ret);
    
    return ret;
    }

TInt CNfcClient::CancelTagReadCallback(TAny * aThis)
    {
    static_cast<CNfcClient *>(aThis)->CancelTagRead();
    return EFalse;
    }
